import ctypes
import os
from PyCmpltrtok.common import get_dir_name_ext, sep
from typing import List
import array
import numpy as np

xdir, xbase, xext = get_dir_name_ext(os.path.abspath(__file__))
xpath = os.path.join(os.path.dirname(xdir), 'libsample.so')
print(xpath)
_mod = ctypes.cdll.LoadLibrary(xpath)
print(_mod)

gcd = _mod.gcd
gcd.argtypes = (ctypes.c_int, ctypes.c_int)
gcd.restype = ctypes.c_int

in_mandel = _mod.in_mandel
in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
in_mandel.restype = ctypes.c_int

_divide = _mod.divide
_divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
_divide.restype = (ctypes.c_int)


def divide(x: int, y: int) -> List[int]:
    remainder = ctypes.c_int()
    quotient = _divide(x, y, remainder)
    return quotient, remainder.value


class DoubleArrayType:
    
    def from_param(self, param):
        typename = type(param).__name__
        if hasattr(self, f'from_{typename}'):
            return getattr(self, f'from_{typename}')(param)
        elif isinstance(param, ctypes.Array):
            return param
        else:
            raise TypeError(f'Cannot convert {typename} to array.')
        
    def from_array(self, param: array.array):
        if param.typecode != 'd':
            raise TypeError(f'It must be an array of doubles.')
        
        # https://docs.python.org/3/library/array.html#array.array.buffer_info
        # The returned numbers are valid as long as the array exists and no length-changing operations are applied to it.
        ptr, _ = param.buffer_info()
        return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
    
    def from_list(self, param):
        val = (ctypes.c_double*len(param))(*param)
        return val
    
    from_tuple = from_list
    
    def from_ndarray(self, param: np.ndarray):
        return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
    
    
_avg = _mod.avg
_avg.argtypes = (DoubleArrayType(), ctypes.c_int)
_avg.restype = ctypes.c_double


def avg(values):
    return _avg(values, len(values))


class Point(ctypes.Structure):
    _fields_ = [
        ('x', ctypes.c_double),
        ('y', ctypes.c_double),
    ]
    
    
distance = _mod.distance
distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
distance.restype = ctypes.c_double

distance_my = _mod.distance_my
distance_my.argtypes = (Point, Point)
distance_my.restype = ctypes.c_double
